Skip to content

Release: Merge develop into main - QR Code features, Sub-app redirects, and Summary Doc fixes - #286

Open
amitsinghsutara wants to merge 12 commits into
mainfrom
develop
Open

Release: Merge develop into main - QR Code features, Sub-app redirects, and Summary Doc fixes#286
amitsinghsutara wants to merge 12 commits into
mainfrom
develop

Conversation

@amitsinghsutara

@amitsinghsutara amitsinghsutara commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR merges the latest features and bug fixes from develop into main.

Key Changes

Features

  • QR Code Functionality (AJ-735): Added QR-code functionality in Cr-container for copying cr_user_id.
  • Local Sub-app Redirects (MR-175): Added a new skill for testing sub-apps locally through localhost hosted sub-apps.

Bug Fixes & Improvements

  • Summary Document Timestamps (MR-187): Fixed an issue by setting updated_at alongside created_at when creating the summary document.
  • Custom Debug ID (MR-175): Added debug-only custom cr_user_id.

CI/CD

  • Includes multiple CI-generated debug APK builds.

Note: The attempt to update the target SDK to 36 was subsequently reverted in this branch.

Summary by CodeRabbit

  • New Features
    • Added an in-app QR code overlay for viewing and copying the current user identifier.
    • Added debug-only support for entering a custom user ID for testing.
  • Bug Fixes
    • Improved identity and host attribution when testing locally.
    • Ensured newly created summaries use a consistent creation and update timestamp.
  • Documentation
    • Added setup guidance for local sub-app testing and URL overrides.
  • Chores
    • Updated the build tooling to a newer Gradle version.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds QR-based identity display, debug custom user-ID support, local sub-app URL redirection, debug loopback networking, and local configuration documentation. It also adds ZXing and uses one timestamp for new summary document fields.

Changes

Debug identity and local development

Layer / File(s) Summary
Build and local development configuration
app/build.gradle, app/src/debug/*, gradle/wrapper/gradle-wrapper.properties, local.properties.example
Build configuration loads local properties, defines debug URL overrides, adds ZXing, enables loopback cleartext HTTP for debug builds, and documents local setup.
QR identity and debug user ID
app/src/main/java/org/curiouslearning/container/MainActivity.java, app/src/main/res/layout/activity_main.xml, app/src/main/res/layout-sw600dp/activity_main.xml, app/src/main/res/layout-sw720dp/activity_main.xml, app/src/main/res/layout/language_popup.xml, app/src/main/res/values/strings.xml
The activity displays the cached user ID as a QR code, supports clipboard copying, and adds a debug-only persisted custom user-ID override.
Local sub-app routing and attribution
app/src/main/java/org/curiouslearning/container/presentation/adapters/WebAppsAdapter.java, app/src/main/java/org/curiouslearning/container/WebApp.java
Debug builds redirect configured sub-app hosts to local origins while retaining deployed URLs for attribution and Feed the Monster detection.
Summary timestamp consistency
app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java
New summary documents use one timestamp for both created_at and updated_at.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Mergeability Score: 🟡 Moderate · up to d48c1

The PR adds QR controls, redirect behavior, and debug configuration, but the current build configuration declares conflicting Android Gradle Plugin versions that can prevent a reliable release build until resolved. Minor follow-up is also needed for analytics identity, localization, and custom-ID persistence.

Sequence Diagram(s)

sequenceDiagram
  participant MainActivity
  participant QR_overlay
  participant QRCodeWriter
  participant ClipboardManager
  MainActivity->>QRCodeWriter: Encode cached user ID
  QRCodeWriter-->>MainActivity: Return QR bitmap
  MainActivity->>QR_overlay: Show QR bitmap and user ID
  QR_overlay->>ClipboardManager: Copy formatted user ID
Loading

Possibly related PRs

Suggested reviewers: dz4va, miguelccodev

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Tests & Lint & Coverage ⚠️ Warning The PR changes production code but no test files. CircleCI runs Fastlane only; no lint result or coverage report exists, and JaCoCo has no 70% threshold. Add tests for the changed features, run the Android lint task and jacocoTestReport, and provide evidence of at least 70% coverage.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the merge and identifies the main QR code, sub-app redirect, and summary document changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/org/curiouslearning/container/MainActivity.java`:
- Line 1102: Register an OnDismissListener for the language dialog after
customUserIdField is initialized, and persist the custom ID through
persistCustomUserId(customUserIdField) when the dialog is dismissed, including
via Back. Keep the existing persistence calls unchanged.

Apply the same fix in
`@app/src/main/java/org/curiouslearning/container/MainActivity.java` around lines
1223 - 1230: Covered by the shared dismissal persistence and handler-refresh
remediation.

In `@app/src/main/java/org/curiouslearning/container/WebApp.java`:
- Around line 105-107: Update the analytics event calls in WebApp so both app
launch and app exit events pass identityUrl() as the web app URL, rather than
appUrl or the local URL. Reuse the existing identityUrl() method and leave other
event fields unchanged.

In `@app/src/main/res/layout/activity_main.xml`:
- Around line 120-122: Replace the hard-coded QR accessibility descriptions and
copy instruction with shared localized string resources across all variants:
app/src/main/res/layout/activity_main.xml lines 120-122, 175-180, and 195-201;
app/src/main/res/layout-sw600dp/activity_main.xml lines 127-129, 163-168, and
181-187; and app/src/main/res/layout-sw720dp/activity_main.xml lines 128-130,
164-169, and 182-188. Add or reuse appropriate entries in the string resources,
preserving the existing show-button, QR-image, and copy-instruction text.

In `@gradle/wrapper/gradle-wrapper.properties`:
- Line 2: Update the Android Gradle Plugin declarations in build.gradle to
remove the outdated 7.3.1 entries, keeping the project consistently on AGP
8.13.1 alongside Gradle 9.2.1 and Java 17.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0195759f-5427-43ac-a601-84ff72d3f30f

📥 Commits

Reviewing files that changed from the base of the PR and between 10d2fa4 and d48c1e5.

⛔ Files ignored due to path filters (2)
  • .claude/skills/test-subapp-locally/SKILL.md is excluded by !**/*.md
  • app/src/main/res/drawable/coaches_icon_cr.png is excluded by !**/*.png
📒 Files selected for processing (15)
  • app/build.gradle
  • app/fastlane/debug_apks/app-debug.apk
  • app/src/debug/AndroidManifest.xml
  • app/src/debug/res/xml/network_security_config.xml
  • app/src/main/java/org/curiouslearning/container/MainActivity.java
  • app/src/main/java/org/curiouslearning/container/WebApp.java
  • app/src/main/java/org/curiouslearning/container/core/subapp/handler/DefaultAppEventPayloadHandler.java
  • app/src/main/java/org/curiouslearning/container/presentation/adapters/WebAppsAdapter.java
  • app/src/main/res/layout-sw600dp/activity_main.xml
  • app/src/main/res/layout-sw720dp/activity_main.xml
  • app/src/main/res/layout/activity_main.xml
  • app/src/main/res/layout/language_popup.xml
  • app/src/main/res/values/strings.xml
  • gradle/wrapper/gradle-wrapper.properties
  • local.properties.example

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
audioPlayer.play(MainActivity.this, R.raw.sound_button_pressed);
persistCustomUserId(customUserIdField);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Persist and apply the custom ID on every dialog dismissal.

The custom-ID dialog remains cancelable, so pressing Back can bypass the existing persistence calls. Persist the field from the cancellation or dismissal path, then refresh the shared handler after editor.apply() so existing WebApp.WebAppInterface instances use the updated value.

📍 Affects 1 file
  • app/src/main/java/org/curiouslearning/container/MainActivity.java#L1102-L1102 (this comment)
  • app/src/main/java/org/curiouslearning/container/MainActivity.java#L1223-L1230
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/org/curiouslearning/container/MainActivity.java` at line
1102, Register an OnDismissListener for the language dialog after
customUserIdField is initialized, and persist the custom ID through
persistCustomUserId(customUserIdField) when the dialog is dismissed, including
via Back. Keep the existing persistence calls unchanged.

Apply the same fix in
`@app/src/main/java/org/curiouslearning/container/MainActivity.java` around lines
1223 - 1230: Covered by the shared dismissal persistence and handler-refresh
remediation.

Comment on lines +105 to +107
private String identityUrl() {
return (localDevOriginalUrl != null && !localDevOriginalUrl.isEmpty()) ? localDevOriginalUrl : appUrl;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the analytics implementation before inspecting its URL handling.
fd -t f 'AnalyticsUtils\.java|AnalyticsUtils\.(kt|java)' app | while IFS= read -r file; do
  ast-grep outline "$file" --items all
  rg -n -C 5 'logEvent|app_launch|app_exit|appUrl|url' "$file"
done

# Find callers and tests that define the event URL contract.
rg -n -C 4 'AnalyticsUtils\.logEvent|logAppLaunchEvent|logAppExitEvent' app

Repository: curiouslearning/CRcontainer

Length of output: 17200


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect how WebApp assigns appUrl and localDevOriginalUrl before analytics calls.
cat -n app/src/main/java/org/curiouslearning/container/WebApp.java | sed -n '1,125p;575,600p'

# Check tests and all AnalyticsUtils.logEvent callers for the expected web_app_url value.
rg -n -C 6 'localDevOriginalUrl|identityUrl|AnalyticsUtils\.logEvent|web_app_url' app/src/test app/src/main app/src/androidTest 2>/dev/null || true

# Model the analytics contract from the source without executing repository code.
python3 - <<'PY'
from pathlib import Path
src = Path("app/src/main/java/org/curiouslearning/container/firebase/AnalyticsUtils.java").read_text()
assert 'bundle.putString("web_app_url", appUrl);' in src
print("AnalyticsUtils.logEvent stores its appUrl parameter as web_app_url.")
PY

Repository: curiouslearning/CRcontainer

Length of output: 24946


Use identityUrl() for analytics events.

AnalyticsUtils.logEvent() stores the URL as web_app_url. Pass identityUrl() to both app launch and app exit events to avoid recording localhost for redirected launches.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/org/curiouslearning/container/WebApp.java` around lines 105
- 107, Update the analytics event calls in WebApp so both app launch and app
exit events pass identityUrl() as the web app URL, rather than appUrl or the
local URL. Reuse the existing identityUrl() method and leave other event fields
unchanged.

Comment on lines +120 to +122
android:src="@drawable/coaches_icon_cr"
android:background="@android:color/transparent"
android:contentDescription="Show QR Code ID"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Move the QR UI text to string resources.

The visible labels and accessibility descriptions are hard-coded in each layout variant. Localized builds cannot translate these QR controls.

  • app/src/main/res/layout/activity_main.xml#L120-L122: replace the show-button description with an @string reference.
  • app/src/main/res/layout/activity_main.xml#L175-L180: replace the QR image description with an @string reference.
  • app/src/main/res/layout/activity_main.xml#L195-L201: replace the copy instruction with an @string reference.
  • app/src/main/res/layout-sw600dp/activity_main.xml#L127-L129: replace the show-button description with an @string reference.
  • app/src/main/res/layout-sw600dp/activity_main.xml#L163-L168: replace the QR image description with an @string reference.
  • app/src/main/res/layout-sw600dp/activity_main.xml#L181-L187: replace the copy instruction with an @string reference.
  • app/src/main/res/layout-sw720dp/activity_main.xml#L128-L130: replace the show-button description with an @string reference.
  • app/src/main/res/layout-sw720dp/activity_main.xml#L164-L169: replace the QR image description with an @string reference.
  • app/src/main/res/layout-sw720dp/activity_main.xml#L182-L188: replace the copy instruction with an @string reference.
📍 Affects 3 files
  • app/src/main/res/layout/activity_main.xml#L120-L122 (this comment)
  • app/src/main/res/layout/activity_main.xml#L175-L180
  • app/src/main/res/layout/activity_main.xml#L195-L201
  • app/src/main/res/layout-sw600dp/activity_main.xml#L127-L129
  • app/src/main/res/layout-sw600dp/activity_main.xml#L163-L168
  • app/src/main/res/layout-sw600dp/activity_main.xml#L181-L187
  • app/src/main/res/layout-sw720dp/activity_main.xml#L128-L130
  • app/src/main/res/layout-sw720dp/activity_main.xml#L164-L169
  • app/src/main/res/layout-sw720dp/activity_main.xml#L182-L188
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/res/layout/activity_main.xml` around lines 120 - 122, Replace
the hard-coded QR accessibility descriptions and copy instruction with shared
localized string resources across all variants:
app/src/main/res/layout/activity_main.xml lines 120-122, 175-180, and 195-201;
app/src/main/res/layout-sw600dp/activity_main.xml lines 127-129, 163-168, and
181-187; and app/src/main/res/layout-sw720dp/activity_main.xml lines 128-130,
164-169, and 182-188. Add or reuse appropriate entries in the string resources,
preserving the existing show-button, QR-image, and copy-instruction text.

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd -t f -a '^(build\.gradle|build\.gradle\.kts|settings\.gradle|settings\.gradle\.kts|libs\.versions\.toml|gradle\.properties)$' . \
  -x rg -n -C2 'com\.android\.application|com\.android\.tools\.build:gradle|agp|org\.gradle\.java\.home|JavaVersion|jvmToolchain' {}

Repository: curiouslearning/CRcontainer

Length of output: 892


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- matching build files ---'
fd -t f -a '^(build\.gradle|build\.gradle\.kts|settings\.gradle|settings\.gradle\.kts|libs\.versions\.toml|gradle\.properties)$' . \
  -x sh -c 'echo "### $1"; cat -n "$1"' sh {}

printf '%s\n' '--- wrapper metadata ---'
fd -t f -a 'gradle-wrapper\.properties$' . \
  -x sh -c 'echo "### $1"; cat -n "$1"' sh {}

Repository: curiouslearning/CRcontainer

Length of output: 13600


🌐 Web query:

Android Gradle Plugin 8.13.1 Gradle 9.2.1 compatibility official documentation

💡 Result:

Yes, Android Gradle Plugin (AGP) 8.13.1 is compatible with Gradle 9.2.1 [1]. Official documentation from Gradle indicates that Gradle 9.2.1 has been tested and is compatible with Android Gradle Plugin versions ranging from 8.4 through 8.13 [1]. While AGP 8.x series was primarily designed for Gradle 8.x, Gradle maintained support for these plugin versions in the 9.x series up to version 9.5 [2]. For your specific configuration: - Android Gradle Plugin 8.13.1 [3] - Gradle 9.2.1 [1] This combination falls within the supported range verified by the Gradle compatibility matrix [1]. As a general best practice, it is recommended to keep both your plugin and Gradle wrapper updated to their latest stable versions to ensure optimal performance and security [4].

Citations:


🌐 Web query:

site:developer.android.com/build/releases Android Gradle Plugin 9.0 Gradle 9.1 compatibility

💡 Result:

Android Gradle Plugin (AGP) 9.0 is compatible with Gradle 9.1.0 and higher [1]. Specifically, AGP 9.0 requires a minimum Gradle version of 9.1.0 [2][1]. For the best performance and compatibility, it is recommended to use the latest versions of both the Android Gradle Plugin and Gradle [1]. As of August 2026, later versions of AGP are available, such as AGP 9.1.1, which requires a minimum Gradle version of 9.3.1 [3][4], and AGP 9.4.0, which requires Gradle 9.6.0 [5].

Citations:


🌐 Web query:

Gradle plugin request already on classpath with a different version buildscript plugins block Android Gradle Plugin

💡 Result:

The error "Plugin request for plugin already on the classpath must not include a version" occurs in Gradle when a plugin is requested with a version in a plugins block, but that plugin is already present on the build classpath (e.g., loaded by the root project, buildSrc, or pluginManagement blocks) [1][2]. Because Gradle does not allow multiple versions of the same plugin to exist in the same build, it rejects any request that attempts to define a version when one is already established [1][3]. To resolve this issue, follow these standard practices: 1. Define the Version Once: Centralize the plugin version in your root build.gradle.kts file or in settings.gradle.kts using the pluginManagement block [1][3]. 2. Use apply false in the Root: When defining the version in the root project, use apply false to add the plugin to the classpath without applying it to the root project itself [1][4][3]. kotlin // In root build.gradle.kts plugins { id("com.android.application") version "8.x.x" apply false } 3. Omit the Version in Subprojects: In your module-level build.gradle.kts files, apply the plugin without specifying the version [1][4][3]. This tells Gradle to use the version already available on the classpath [4]. kotlin // In module build.gradle.kts plugins { id("com.android.application") } If you are using Version Catalogs (libs.versions.toml), this issue often arises when using alias(libs.plugins.xxx) in modules, as alias typically includes the version [5][4]. In such cases, if the plugin is already on the classpath, you may need to use id to reference the plugin ID directly instead of using the alias, or ensure the version is only defined in the root where it is first applied [5][6][4]. For the Android Gradle Plugin (AGP), strictly enforce the same version across all modules and included builds [7]. AGP contains internal logic that specifically checks for version consistency and will throw an error if multiple versions are detected within the same build process [7].

Citations:


🌐 Web query:

site:docs.gradle.org plugin already on the classpath with a different version

💡 Result:

The error "plugin already on the classpath with a different version" typically occurs in build tools like Gradle when a project attempts to load the same plugin with conflicting version requirements [1]. Because plugins share the same classloader, Gradle and other build systems cannot safely load two different versions of the same plugin simultaneously [1]. To troubleshoot and resolve this issue, consider the following approaches based on the dependency graph: 1. Identify the Conflict: Use built-in diagnostic tools to understand where the conflicting versions are coming from. In Gradle, the dependencyInsight task is the primary tool for this [2]. You can run:./gradlew dependencyInsight --configuration <configuration_name> --dependency <plugin_id> This report will show you the "Selection reasons," identifying which parts of your project or transitive dependencies are requesting the different versions [2]. 2. Align Plugin Versions: The most common fix is to ensure all parts of your build configuration request the same version of the plugin [3][4]. If a transitive dependency is pulling in an older or different version, you can force a specific version in your top-level build script or settings file to override it [3][5]. 3. Use Plugin Management: If you are managing multiple sub-projects, define the plugin versions in your settings.gradle (or settings.gradle.kts) file using the pluginsManagement block [6]. This centralizes the version definition, preventing individual modules from accidentally requesting conflicting versions [6]. 4. Check for Capability Conflicts: In some cases, the error relates to "capabilities" where different modules (or different versions of the same relocated module) provide the same feature [7][8]. If this is the cause, you may need to use component metadata rules to explicitly declare them as mutually exclusive or choose a preferred implementation [7][9]. If you are developing a plugin, be aware that plugins can introduce complex dependency graphs [1]. Ensure that your plugin does not transitively include a version of itself or a core library that conflicts with the host project's requirements [1].

Citations:


Resolve the Android Gradle Plugin version conflict.

Gradle 9.2.1, Android Gradle Plugin 8.13.1, and Java 17 are compatible. However, build.gradle declares Android Gradle Plugin 8.13.1 and 7.3.1. Remove or update the 7.3.1 declarations before merging.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gradle/wrapper/gradle-wrapper.properties` at line 2, Update the Android
Gradle Plugin declarations in build.gradle to remove the outdated 7.3.1 entries,
keeping the project consistently on AGP 8.13.1 alongside Gradle 9.2.1 and Java
17.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants